let threeAudioStatus = false;
function getRandomColor() {
  var r = Math.floor(Math.random() * 256);
  var g = Math.floor(Math.random() * 256);
  var b = Math.floor(Math.random() * 256);

  var hex = ((r << 16) | (g << 8) | b).toString(16);

  while (hex.length < 6) {
    hex = '0' + hex;
  }

  return '#' + hex;
}

function AudioSystem(audioUrl, loadedCallback = () => {}) {
  var listener = new THREE.AudioListener();
  var sound = new THREE.Audio(listener);
  var loader = new THREE.AudioLoader();

  var FFT_SIZE = 4192;
  var MASTER_VOLUME = 1;
  var audioContext = sound.context;
  var analyser = audioContext.createAnalyser();
  listener.setMasterVolume(MASTER_VOLUME);
  loader.load(audioUrl, function (buffer) {
    console.log('audio loaded.');
    const playingStatusListener = loadedCallback(true);
    threeAudioStatus = true;
    sound.setBuffer(buffer);
    sound.setLoop(false);
    sound.setVolume(1);
    sound.getOutput().connect(analyser);
    sound.play();

    window.soundwave && window.soundwave.transitionShowSoundwave();
    sound._isPlaying = sound.isPlaying;

    Object.defineProperty(sound, 'isPlaying', {
      get() {
        return sound._isPlaying;
      },
      set(newVal) {
        playingStatusListener && playingStatusListener(newVal);
        sound._isPlaying = newVal;
      },
    });
  });

  this.waveform = new Uint8Array(analyser.frequencyBinCount);
  this.frequency = new Uint8Array(analyser.frequencyBinCount);

  this.sound = sound;
  this.analyser = analyser;

  audioUniforms.waveform.value = new THREE.DataTexture(
    this.waveform,
    FFT_SIZE / 2,
    1,
    THREE.LuminanceFormat,
  );
  audioUniforms.frequency.value = new THREE.DataTexture(
    this.frequency,
    FFT_SIZE / 2,
    1,
    THREE.LuminanceFormat,
  );
}

AudioSystem.prototype.start = function () {
  this.sound.play();
};

AudioSystem.prototype.update = function () {
  this.analyser.getByteTimeDomainData(this.waveform);
  this.analyser.getByteFrequencyData(this.frequency);
};

AudioSystem.prototype.play = function () {
  this.sound.play();
};

AudioSystem.prototype.pause = function () {
  this.sound.source && this.sound.pause();
};
AudioSystem.prototype.stop = function () {
  this.pause();
  this.sound.source && this.sound.stop();
  threeAudioStatus = false;
};

THREE.ShaderChunk.app =
  '\n\nuniform vec2 screen;\nuniform vec2 mouse;\nuniform float dt;\nuniform float time;\n\n';

THREE.ShaderChunk.audio =
  '\n\nuniform sampler2D waveform;\nuniform sampler2D frequency;\n\nuniform float instantVolume;\nuniform float smoothedVolume;\n\nfloat sampleAudioTexture(sampler2D tex, float coord) {\n\n  return texture2D(tex, vec2(coord, 0.0)).r;\n\n}\n\nfloat averageAudioTexture(sampler2D tex, float coord, float width) {\n\n  float result = 0.0;\n\n  for (int i = 0; i < 4; ++i) {\n    \n    result += sampleAudioTexture(tex, coord + width * (float(i) - 2.0)) / 4.0;\n    \n  }\n  \n  return result;\n  \n}\n\n';
THREE.ShaderChunk.rotation_matrix =
  '\n\nmat4 createRotationMatrix(vec3 axis, float angle) {\n  \n  axis = normalize(axis);\n  float s = sin(angle);\n  float c = cos(angle);\n  float oc = 1.0 - c;\n\n  return mat4(oc * axis.x * axis.x + c,           oc * axis.x * axis.y - axis.z * s,  oc * axis.z * axis.x + axis.y * s,  0.0,\n              oc * axis.x * axis.y + axis.z * s,  oc * axis.y * axis.y + c,           oc * axis.y * axis.z - axis.x * s,  0.0,\n              oc * axis.z * axis.x - axis.y * s,  oc * axis.y * axis.z + axis.x * s,  oc * axis.z * axis.z + c,           0.0,\n              0.0,                                0.0,                                0.0,                                1.0);\n\n}\n\n';
THREE.ShaderChunk.hash =
  '\n\n// Pseudorandom hash functions\n// https://www.shadertoy.com/view/4djSRW\n\n// Use these for integer stepped ranges, ie Value-Noise/Perlin noise functions.\n#define HASHSCALE1 0.1031\n#define HASHSCALE3 vec3(0.1031, 0.1030, 0.0973)\n#define HASHSCALE4 vec4(1031, 0.1030, 0.0973, 0.1099)\n\n// Use these for smaller input rangers like audio tick or 0-1 UVs.\n// #define HASHSCALE1 443.8975\n// #define HASHSCALE3 vec3(443.897, 441.423, 437.195)\n// #define HASHSCALE4 vec4(443.897, 441.423, 437.195, 444.129)\n\nfloat hash11(float p)\n{\n  vec3 o = fract(vec3(p,p,p) * HASHSCALE1);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.x + o.y) * o.z);\n}\n\nfloat hash12(vec2 p)\n{\n  vec3 o = fract(p.xyx * HASHSCALE1);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.x + o.y) * o.z);\n}\n\nfloat hash13(vec3 p)\n{\n  vec3 o = fract(p.xyz * HASHSCALE1);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.x + o.y) * o.z);\n}\n\nvec2 hash21(float p)\n{\n  vec3 o = fract(vec3(p,p,p) * HASHSCALE3);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.xx + o.yz) * o.zy);\n}\n\nvec2 hash22(vec2 p)\n{\n  vec3 o = fract(p.xyx * HASHSCALE3);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.xx + o.yz) * o.zy);\n\n}\n\nvec2 hash23(vec3 p)\n{\n  vec3 o = fract(p.xyz * HASHSCALE3);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.xx+o.yz) * o.zy);\n}\n\nvec3 hash31(float p)\n{\n  vec3 o = fract(vec3(p,p,p) * HASHSCALE3);\n  o += dot(o, o.yzx + 19.19);\n  return fract((o.xxy + o.yzz) * o.zyx); \n}\n\nvec3 hash32(vec2 p)\n{\n  vec3 o = fract(p.xyx * HASHSCALE3);\n  o += dot(o, o.yxz + 19.19);\n  return fract((o.xxy + o.yzz) * o.zyx);\n}\n\nvec3 hash33(vec3 p)\n{\n  vec3 o = fract(p.xyz * HASHSCALE3);\n  o += dot(o, o.yxz + 19.19);\n  return fract((o.xxy + o.yxx) * o.zyx);\n}\n\nvec4 hash41(float p)\n{\n  vec4 o = fract(vec4(p,p,p,p) * HASHSCALE4);\n  o += dot(o, o.wzxy + 19.19);\n  return fract((o.xxyz + o.yzzw) * o.zywx);\n}\n\nvec4 hash42(vec2 p)\n{\n  vec4 o = fract(p.xyxy * HASHSCALE4);\n  o += dot(o, o.wzxy + 19.19);\n  return fract((o.xxyz + o.yzzw) * o.zywx);\n}\n\nvec4 hash43(vec3 p)\n{\n  vec4 o = fract(p.xyzx * HASHSCALE4);\n  o += dot(o, o.wzxy + 19.19);\n  return fract((o.xxyz + o.yzzw) * o.zywx);\n}\n\nvec4 hash44(vec4 p)\n{\n  vec4 o = fract(p.xyzw * HASHSCALE4);\n  o += dot(o, o.wzxy + 19.19);\n  return fract((o.xxyz + o.yzzw) * o.zywx);\n}\n\n';
THREE.ShaderChunk.noise =
  '\n\n#include <hash>\n\n// http://iquilezles.org/www/articles/gradientnoise/gradientnoise.htm\n\n// returns 3D value noise\nfloat noise(in vec3 x)\n{\n    // grid\n    vec3 p = floor(x);\n    vec3 w = fract(x);\n    \n    // quintic interpolant\n    vec3 u = w*w*w*(w*(w*6.0-15.0)+10.0);\n    \n    // gradients\n    vec3 ga = hash33( p+vec3(0.0,0.0,0.0) );\n    vec3 gb = hash33( p+vec3(1.0,0.0,0.0) );\n    vec3 gc = hash33( p+vec3(0.0,1.0,0.0) );\n    vec3 gd = hash33( p+vec3(1.0,1.0,0.0) );\n    vec3 ge = hash33( p+vec3(0.0,0.0,1.0) );\n    vec3 gf = hash33( p+vec3(1.0,0.0,1.0) );\n    vec3 gg = hash33( p+vec3(0.0,1.0,1.0) );\n    vec3 gh = hash33( p+vec3(1.0,1.0,1.0) );\n    \n    // projections\n    float va = dot( ga, w-vec3(0.0,0.0,0.0) );\n    float vb = dot( gb, w-vec3(1.0,0.0,0.0) );\n    float vc = dot( gc, w-vec3(0.0,1.0,0.0) );\n    float vd = dot( gd, w-vec3(1.0,1.0,0.0) );\n    float ve = dot( ge, w-vec3(0.0,0.0,1.0) );\n    float vf = dot( gf, w-vec3(1.0,0.0,1.0) );\n    float vg = dot( gg, w-vec3(0.0,1.0,1.0) );\n    float vh = dot( gh, w-vec3(1.0,1.0,1.0) );\n\t\n    // interpolation\n    return va + \n           u.x*(vb-va) + \n           u.y*(vc-va) + \n           u.z*(ve-va) + \n           u.x*u.y*(va-vb-vc+vd) + \n           u.y*u.z*(va-vc-ve+vg) + \n           u.z*u.x*(va-vb-ve+vf) + \n           u.x*u.y*u.z*(-va+vb+vc-vd+ve-vf-vg+vh);\n}\n\n// returns 3D value noise (in .x)  and its derivatives (in .yzw)\nvec4 noised( in vec3 x )\n{\n    // grid\n    vec3 p = floor(x);\n    vec3 w = fract(x);\n    \n    // quintic interpolant\n    vec3 u = w*w*w*(w*(w*6.0-15.0)+10.0);\n    vec3 du = 30.0*w*w*(w*(w-2.0)+1.0);\n    \n    // gradients\n    vec3 ga = hash33( p+vec3(0.0,0.0,0.0) );\n    vec3 gb = hash33( p+vec3(1.0,0.0,0.0) );\n    vec3 gc = hash33( p+vec3(0.0,1.0,0.0) );\n    vec3 gd = hash33( p+vec3(1.0,1.0,0.0) );\n    vec3 ge = hash33( p+vec3(0.0,0.0,1.0) );\n    vec3 gf = hash33( p+vec3(1.0,0.0,1.0) );\n    vec3 gg = hash33( p+vec3(0.0,1.0,1.0) );\n    vec3 gh = hash33( p+vec3(1.0,1.0,1.0) );\n    \n    // projections\n    float va = dot( ga, w-vec3(0.0,0.0,0.0) );\n    float vb = dot( gb, w-vec3(1.0,0.0,0.0) );\n    float vc = dot( gc, w-vec3(0.0,1.0,0.0) );\n    float vd = dot( gd, w-vec3(1.0,1.0,0.0) );\n    float ve = dot( ge, w-vec3(0.0,0.0,1.0) );\n    float vf = dot( gf, w-vec3(1.0,0.0,1.0) );\n    float vg = dot( gg, w-vec3(0.0,1.0,1.0) );\n    float vh = dot( gh, w-vec3(1.0,1.0,1.0) );\n\t\n    // interpolation\n    float v = va + \n              u.x*(vb-va) + \n              u.y*(vc-va) + \n              u.z*(ve-va) + \n              u.x*u.y*(va-vb-vc+vd) + \n              u.y*u.z*(va-vc-ve+vg) + \n              u.z*u.x*(va-vb-ve+vf) + \n              u.x*u.y*u.z*(-va+vb+vc-vd+ve-vf-vg+vh);\n              \n    vec3 d = ga + \n             u.x*(gb-ga) + \n             u.y*(gc-ga) + \n             u.z*(ge-ga) + \n             u.x*u.y*(ga-gb-gc+gd) + \n             u.y*u.z*(ga-gc-ge+gg) + \n             u.z*u.x*(ga-gb-ge+gf) + \n             u.x*u.y*u.z*(-ga+gb+gc-gd+ge-gf-gg+gh) +   \n             \n             du * (vec3(vb-va,vc-va,ve-va) + \n                   u.yzx*vec3(va-vb-vc+vd,va-vc-ve+vg,va-vb-ve+vf) + \n                   u.zxy*vec3(va-vb-ve+vf,va-vb-vc+vd,va-vc-ve+vg) + \n                   u.yzx*u.zxy*(-va+vb+vc-vd+ve-vf-vg+vh));\n                   \n    return vec4( v, d );                   \n}\n';

THREE.ShaderChunk.project_billboard =
  '\n\n#include <rotation_matrix>\n\n/**\n * Project a vertex as a billboard in camera-space.\n */\nvec3 projectBillboardVertex(vec3 vertex, float scale) {\n\n  vec3 cameraRight = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);\n  vec3 cameraUp    = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);\n\n  return (cameraRight * vertex.x * scale) + (cameraUp * vertex.y * scale);\n}\n\n/**\n * Project a vertex as a billboard in camera-space, at an angle.\n */\nvec3 projectBillboardVertex(vec3 vertex, float angle, float scale) {\n  \n  vec3 cameraForward = vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);\n  vec3 cameraRight   = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);\n  vec3 cameraUp      = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);\n\n  mat4 billboardRotation = createRotationMatrix(cameraForward, angle);\n  vec3 billboardRight    = (billboardRotation * vec4(cameraRight, 1.0)).xyz;\n  vec3 billboardUp       = (billboardRotation * vec4(cameraUp, 1.0)).xyz;\n  \n  return (billboardRight * vertex.x * scale) + (billboardUp * vertex.y * scale);\n\n}\n \n';

var SoundwaveTrampolineShader = {};

SoundwaveTrampolineShader.vertex =
  "\n\n#include <project_billboard>\n#include <common>\n#include <app>\n#include <audio>\n#include <noise>\n\nuniform float radius;\nuniform float radiusMultiplier;\nuniform float rotation;\nuniform float perturbMultiplier;\n\nuniform float audioFrequencyFactor;\nuniform float audioVolumeFactor;\nuniform float audioScaleFactor;\nuniform float audioScaleExponent;\n\nuniform float particleScale;\nuniform float particleCount;\n\nattribute float pid;\nattribute vec3 seed;\n\nvoid main() {\n  \n  float norm = pid / particleCount;\n  float theta = norm * PI2;\n  \n  // arrange the particles in a circle\n  vec2 circle = vec2(sin(theta + rotation * PI2), cos(theta + rotation * PI2));\n  \n  #ifdef USE_FREQUENCY_OFFSET\n    \n    float frequencyCoord = seed.x; // the trampoline uses random frequency coordinates\n    \n    frequencyCoord *= 0.6; // ignore upper (boring) frequencies\n    \n    float frequencySample = sampleAudioTexture(frequency, frequencyCoord);\n    float frequencyOffset = frequencySample * audioFrequencyFactor;\n    \n    frequencyOffset *= seed.y;\n    \n  #endif\n  \n  #ifdef USE_VOLUME_OFFSET\n    \n    float volumeOffset = smoothedVolume * audioVolumeFactor;\n    \n    volumeOffset *= seed.y;    \n    \n  #endif\n  \n  // Sum offsets and perturb the particle position.\n  \n  float offset = 0.0;\n  \n  #ifdef USE_FREQUENCY_OFFSET\n  offset += frequencyOffset;\n  #endif\n  \n  #ifdef USE_VOLUME_OFFSET\n  offset += volumeOffset;\n  #endif\n  \n  vec3 particlePosition = vec3(circle, 0) * radius * radiusMultiplier + vec3(0, 0, -1) * offset * perturbMultiplier;\n  \n  // Compute the particle scale.\n  \n  float scale = particleScale;\n  scale += pow(max(0.0, offset * audioScaleFactor), audioScaleExponent);\n  scale /= length(cameraPosition - particlePosition);\n  \n  // Project the vertex as a billboard in model space, then add it to the particle position in\n  // world space. We don't need to use the model matrix here because the particle's position\n  // is always computed in world space.\n  \n  vec3 worldPosition = particlePosition + projectBillboardVertex(position, scale);\n  \n  gl_Position = projectionMatrix * viewMatrix * vec4(worldPosition, 1.0);\n \n}\n\n";

SoundwaveTrampolineShader.fragment =
  '\n\nuniform vec3 color;\n\nvoid main() {\n\n  gl_FragColor = vec4(color, 1.0);\n\n}\n\n';

var SoundwaveRingShader = {};

SoundwaveRingShader.vertex =
  '\n\n#include <project_billboard>\n#include <common>\n#include <app>\n#include <audio>\n#include <noise>\n\nuniform float radius;\nuniform float radiusMultiplier;\nuniform float rotation;\nuniform float dampenExponent;\nuniform float perturbMultiplier;\n\nuniform float audioFrequencyFactor;\nuniform float audioVolumeFactor;\nuniform float audioScaleFactor;\nuniform float audioScaleExponent;\n\nuniform vec3 noiseParams;\n\nuniform float sineFrequency;\nuniform float sineAmplitude;\nuniform float sineTimescale;\n\nuniform float particleScale;\nuniform float particleCount;\n\nattribute float pid;\nattribute vec3 seed;\n\nvoid main() {\n  \n  float norm = pid / particleCount;\n  float theta = norm * PI2;\n  \n  // arrange the particles in a circle\n  vec2 circle = vec2(sin(theta + rotation * PI2), cos(theta + rotation * PI2));\n  \n  // dampen values when norm is close to 0 or 1\n  float dampen = pow(abs(sin(norm * PI)), dampenExponent);\n  \n  #ifdef USE_FREQUENCY_OFFSET\n    \n    float frequencyCoord = norm;\n    \n    #ifdef USE_MIRRORED_FREQUENCY\n    frequencyCoord = abs(frequencyCoord * 2.0 - 1.0);\n    #endif\n    \n    frequencyCoord *= 0.6; // ignore upper (boring) frequencies\n    \n    float frequencySample = sampleAudioTexture(frequency, frequencyCoord);\n    float frequencyOffset = frequencySample * audioFrequencyFactor;\n    \n  #endif\n  \n  #ifdef USE_VOLUME_OFFSET\n    \n    float volumeOffset = smoothedVolume * audioVolumeFactor;\n    \n  #endif\n  \n  #ifdef USE_SINE_OFFSET\n    \n    float sineOffset = sin(theta * sineFrequency + time * sineTimescale) * sineAmplitude * smoothedVolume * dampen;\n    \n  #endif\n  \n  #ifdef USE_NOISE_OFFSET\n    \n    // Compute some gradient noise using the circle coordinates and the application time. We use the\n    // absolute value of the x coordinate to make the noise horizontally symmetrical. Unfortunately\n    // this makes the offset discontinuous around x == 0. Fortunately the x derivative *is*\n    // continuous there, so we mix the value noise with the x derivative.\n    \n    vec3 noiseCoord = vec3(0.0);\n    \n    noiseCoord.x = max(0.25, smoothedVolume) * noiseParams.x * abs(circle.x);\n    noiseCoord.y = max(0.25, smoothedVolume) * noiseParams.y * circle.y;\n    noiseCoord.z = max(0.10, smoothedVolume) * noiseParams.z * sin(time);\n    \n    vec4 noiseDerivatives = noised(noiseCoord);\n    float noiseOffset = mix(noiseDerivatives.x, noiseDerivatives.y * 0.2, dampen) * max(0.1, dampen);  \n    \n  #endif\n  \n  // Sum offsets and perturb the particle position.\n  \n  float offset = 0.0;\n  \n  #ifdef USE_FREQUENCY_OFFSET\n  offset += frequencyOffset;\n  #endif\n  \n  #ifdef USE_VOLUME_OFFSET\n  offset += volumeOffset;\n  #endif\n  \n  #ifdef USE_NOISE_OFFSET\n  offset += noiseOffset;\n  #endif\n  \n  #ifdef USE_SINE_OFFSET\n  offset += sineOffset;\n  #endif\n  \n  vec3 particlePosition = vec3(circle, 0) * radius * radiusMultiplier + vec3(circle, 0) * offset * perturbMultiplier;\n  \n  // Compute the particle scale.\n  \n  float scale = particleScale;\n  scale += pow(max(0.0, offset * audioScaleFactor), audioScaleExponent);\n  scale /= length(cameraPosition - particlePosition);\n  \n  vec3 transformed = particlePosition + projectBillboardVertex(position, scale);\n  \n  #include <project_vertex>\n \n}\n\n';
SoundwaveRingShader.fragment =
  '\n\nuniform vec3 color;\n\nvoid main() {\n\n  gl_FragColor = vec4(color, 1.0);\n\n}\n\n';

var audioUniforms = {
  waveform: {
    value: null,

    type: 't',
  },

  frequency: {
    value: null,

    type: 't',
  },

  instantVolume: {
    value: 0,
  },

  smoothedVolume: {
    value: 0,
  },
};

var appUniforms = {
  screen: {
    value: new THREE.Vector2(),
  },

  mouse: {
    value: new THREE.Vector2(),
  },

  time: {
    value: 0,
  },

  dt: {
    value: 0,
  },
};

function ParticleBufferGeometry(options) {
  options = Object.assign(
    {
      particleCount: 1e3,
    },
    options,
  );

  if (!options.particleGeometry)
    throw new Error('You must provide a particle geometry. 你必须提供粒子几何');

  var t = void 0;
  if (typeof options.particleGeometry == 'function') {
    t = options.particleGeometry;
  } else if (Array.isArray(options.particleGeometry)) {
    t = function () {
      return options.particleGeometry[THREE.Math.randInt(0, options.particleGeometry.length - 1)];
    };
  } else {
    t = function () {
      return options.particleGeometry;
    };
  }

  for (var i = [], n = [], r = [], o = [], a = 0; a < options.particleCount; a++) {
    for (var s = t(), l = s.indexArray.length, h = o.length / 3, u = 0; u < l; u++)
      r.push(s.indexArray[u] + h);

    for (
      var p = Math.random(),
        d = Math.random(),
        f = Math.random(),
        m = s.vertexArray.length / 3,
        v = 0;
      v < m;
      v++
    ) {
      i.push(a), n.push(p, d, f);
      var g = s.vertexArray[3 * v + 0],
        y = s.vertexArray[3 * v + 1],
        _ = s.vertexArray[3 * v + 2];
      o.push(g, y, _);
    }
  }

  var b = new Uint32Array(r),
    w = new THREE.BufferAttribute(b, 1),
    x = new Float32Array(i),
    T = new THREE.BufferAttribute(x, 1),
    S = new Float32Array(n),
    E = new THREE.BufferAttribute(S, 3),
    P = new Float32Array(o),
    M = new THREE.BufferAttribute(P, 3),
    A = new THREE.BufferGeometry();

  A.setIndex(w);

  A.addAttribute('pid', T);
  A.addAttribute('seed', E);
  A.addAttribute('position', M);

  Object.defineProperties(A, {
    particleCount: {
      value: options.particleCount,
      writable: !1,
    },
  });

  return A;
}

function FannedCircleParticleGeometry() {
  for (
    var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 3,
      t = [],
      i = [],
      n = 0;
    n < e;
    n++
  ) {
    var r = (2 * n * Math.PI) / e;

    i.push(Math.sin(r), Math.cos(r), 0);
  }

  for (var o = 0; o < e - 2; o++) t.push(o + 2, o + 1, 0);

  return {
    indexArray: t,
    vertexArray: i,
  };
}

function TetrahedronParticleGeometry() {
  return {
    indexArray: [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1],
    vertexArray: [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1],
  };
}

function SoundwaveTrampoline(options) {
  options = Object.assign(
    {
      radius: 2,

      audioFrequencyFactor: 0.5,

      audioVolumeFactor: 0.2,

      audioScaleFactor: 0.35,

      audioScaleExponent: 2,
      particleCount: 1e3,
    },
    options,
  );

  var geometry = ParticleBufferGeometry({
    particleCount: options.particleCount,

    particleGeometry: FannedCircleParticleGeometry(18),
  });

  var uniforms = Object.assign(
    {
      color: {
        value: options.color,
      },

      radius: {
        value: options.radius,
      },

      radiusMultiplier: {
        value: 1,
      },

      perturbMultiplier: {
        value: 1,
      },

      audioFrequencyFactor: {
        value: options.audioFrequencyFactor,
      },

      audioVolumeFactor: {
        value: options.audioVolumeFactor,
      },

      audioScaleFactor: {
        value: options.audioScaleFactor,
      },

      audioScaleExponent: {
        value: options.audioScaleExponent,
      },

      particleCount: {
        value: geometry.particleCount,
      },

      particleScale: {
        value: 0.05,
      },
    },
    audioUniforms,
    appUniforms,
  );

  var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    defines: {
      USE_FREQUENCY_OFFSET: !0,
      USE_VOLUME_OFFSET: !0,
    },

    vertexShader: SoundwaveTrampolineShader.vertex,

    fragmentShader: SoundwaveTrampolineShader.fragment,
  });

  var mesh = new THREE.Mesh(geometry, material);
  mesh.name = 'Soundwave Trampoline';

  mesh.frustumCulled = !1;

  mesh.matrixAutoUpdate = !1;

  this.object3D = mesh;
}

function createSoundwaveRing(options) {
  options = Object.assign(
    {
      radius: 1.2,
      radiusMultiplier: 1,
      rotation: 0.5,
      dampenExponent: 2.5,
      audioFrequencyFactor: 0.5,
      audioVolumeFactor: 0.2,
      audioScaleFactor: 0.35,
      audioScaleExponent: 2,
      noiseParams: new THREE.Vector3(9, 7, 17),
      sineFrequency: 30,
      sineAmplitude: 0.09,
      sineTimescale: 10,
    },
    options,
  );

  var geometry = ParticleBufferGeometry({
    particleCount: 800,

    particleGeometry: FannedCircleParticleGeometry(3),
  });

  var uniforms = Object.assign(
    {
      color: {
        value: options.color,
      },

      radius: {
        value: options.radius,
      },

      radiusMultiplier: {
        value: options.radiusMultiplier,
      },

      rotation: {
        value: options.rotation,
      },

      dampenExponent: {
        value: options.dampenExponent,
      },

      perturbMultiplier: {
        value: 1,
      },

      audioFrequencyFactor: {
        value: options.audioFrequencyFactor,
      },

      audioVolumeFactor: {
        value: options.audioVolumeFactor,
      },

      audioScaleFactor: {
        value: options.audioScaleFactor,
      },

      audioScaleExponent: {
        value: options.audioScaleExponent,
      },

      noiseParams: {
        value: options.noiseParams,
      },

      sineFrequency: {
        value: options.sineFrequency,
      },

      sineAmplitude: {
        value: options.sineAmplitude,
      },

      sineTimescale: {
        value: options.sineTimescale,
      },

      particleCount: {
        value: geometry.particleCount,
      },

      particleScale: {
        value: options.particleScale,
      },
    },

    audioUniforms,

    appUniforms,
  );

  var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    defines: {
      USE_MIRRORED_FREQUENCY: !0,
      USE_FREQUENCY_OFFSET: !0,
      USE_VOLUME_OFFSET: !0,
      USE_NOISE_OFFSET: !0,
      USE_SINE_OFFSET: !0,
    },

    vertexShader: SoundwaveRingShader.vertex,

    fragmentShader: SoundwaveRingShader.fragment,

    transparent: true,
  });

  var mesh = new THREE.Mesh(geometry, material);
  mesh.name = 'Soundwave Ring';
  mesh.frustumCulled = !1;
  mesh.matrixAutoUpdate = !1;

  return mesh;
}

function SoundwaveRing(options) {
  this.index = options.index;
  this.object3D = createSoundwaveRing(options);
}

function Soundwave() {
  this.object3D = new THREE.Object3D();
  const r = getRandomColor;
  const r1 = r();
  var children = [
    new SoundwaveRing({
      index: 0,

      color: new THREE.Color(r()),
      radius: 1.6,
      rotation: 0.6,
      dampenExponent: 0.6,
      audioFrequencyFactor: 0.35,
      audioVolumeFactor: 0,
      audioScaleFactor: 0.68,
      audioScaleExponent: 2.2,
      noiseParams: new THREE.Vector3(9.6, 7.3, 9),

      sineFrequency: 13,
      sineAmplitude: 0.33,
      sineTimescale: 19,
      particleScale: 0.1,
    }),

    new SoundwaveRing({
      index: 1,

      color: new THREE.Color(r()),
      radius: 1.3,
      rotation: 0.18,
      dampenExponent: 2.5,
      audioFrequencyFactor: 0.5,
      audioVolumeFactor: 0.2,
      audioScaleFactor: 0.35,
      audioScaleExponent: 2,
      noiseParams: new THREE.Vector3(9, 7, 17),
      sineFrequency: 30,
      sineAmplitude: 0.09,
      sineTimescale: 10,
      particleScale: 0.1,
    }),

    new SoundwaveRing({
      index: 2,

      color: new THREE.Color(r()),
      radius: 1.2,
      rotation: 0.7,
      dampenExponent: 0.7,
      audioFrequencyFactor: 0.7,
      audioVolumeFactor: 0.5,
      audioScaleFactor: 0.3,
      audioScaleExponent: 1.5,
      noiseParams: new THREE.Vector3(4, 7, 17),
      sineFrequency: 29,
      sineAmplitude: 0.08,
      sineTimescale: 8,
      particleScale: 0.1,
    }),

    new SoundwaveRing({
      index: 3,

      color: new THREE.Color(r()),
      radius: 1.2,
      rotation: 1,
      dampenExponent: 9,
      audioFrequencyFactor: 0.5,
      audioVolumeFactor: 0.2,
      audioScaleFactor: 0.35,
      audioScaleExponent: 2,
      noiseParams: new THREE.Vector3(9, 7, 17),
      sineFrequency: 30,
      sineAmplitude: 0.09,
      sineTimescale: 10,
      particleScale: 0.1,
    }),

    new SoundwaveRing({
      index: 4,

      color: new THREE.Color(r()),
      radius: 1.0,
      rotation: 0.6,
      dampenExponent: 0.6,
      audioFrequencyFactor: 0.6,
      audioVolumeFactor: 0.55,
      audioScaleFactor: 0.35,
      audioScaleExponent: 2,
      noiseParams: new THREE.Vector3(9, 7, 17),
      sineFrequency: 31,
      sineAmplitude: 0.09,
      sineTimescale: 10,
      particleScale: 0.1,
    }),

    new SoundwaveTrampoline({
      color: new THREE.Color(r()),
      radius: 0.6,
      audioFrequencyFactor: 0.1,
      audioVolumeFactor: 10,
      audioScaleFactor: 0.1,
      audioScaleExponent: 5.5,
      particleCount: 200,
    }),

    new SoundwaveTrampoline({
      color: new THREE.Color(r()),
      radius: 1.6,
      audioFrequencyFactor: 0.01,
      audioVolumeFactor: 30,
      audioScaleFactor: 0.05,
      audioScaleExponent: 40,
      particleCount: 600,
    }),

    new SoundwaveTrampoline({
      color: new THREE.Color(r()),
      radius: 1.6,
      audioFrequencyFactor: 0.15,
      audioVolumeFactor: 40,
      audioScaleFactor: 0.1,
      audioScaleExponent: 50,
      particleCount: 100,
    }),

    new SoundwaveTrampoline({
      color: new THREE.Color(r()),
      radius: 1.6,
      audioFrequencyFactor: 0.2,
      audioVolumeFactor: 50,
      audioScaleFactor: 0.1,
      audioScaleExponent: 55,
      particleCount: 100,
    }),
  ];

  children.forEach((child) => {
    this.object3D.add(child.object3D);
  });
}

Soundwave.prototype.transitionShowSoundwave = function () {
  var timeline = new TimelineMax();

  var object3D = this.object3D;
  object3D.visible = true;

  app?.camera?.target?.set(0, 0, 0);

  app?.camera?.position?.set(0, 0, -10);

  object3D.children.forEach((child) => {
    timeline.from(
      child.material.uniforms.particleScale,
      1,
      {
        value: 10,
        ease: Power3.easeOut,
      },
      0,
    );

    timeline.from(
      child.material.uniforms.radiusMultiplier,
      1,
      {
        value: 10,
        ease: Power3.easeOut,
      },
      0,
    );

    timeline.from(
      child.material.uniforms.perturbMultiplier,
      1,
      {
        value: 10,
        ease: Power3.easeOut,
      },
      0,
    );
  });
};

Soundwave.prototype.transitionExplodeSoundwave = function () {
  var timeline = new TimelineMax();
  var object3D = this.object3D;

  object3D.visible = true;
  object3D.children.forEach((child, index) => {
    var delay = 0.1 * index;

    timeline.add(
      [
        TweenLite.to(child.material.uniforms.radiusMultiplier, 0.4, {
          value: 0.7,
          ease: Power2.easeOut,
        }),
        TweenLite.to(child.material.uniforms.radiusMultiplier, 0.8, {
          value: 4,
          ease: Power3.easeOut,
        }),
      ],
      delay,
      'sequence',
    );

    timeline.add(
      [
        TweenLite.to(child.material.uniforms.perturbMultiplier, 0.4, {
          value: 0.4,
          ease: Power2.easeOut,
        }),
        TweenLite.to(child.material.uniforms.perturbMultiplier, 0.8, {
          value: 1,
          ease: Power3.easeOut,
        }),
      ],
      delay,
      'sequence',
    );
  });

  timeline.call(() => {
    return (object3D.visible = !1);
  });
};

function CameraDolly() {
  this.object3D = new THREE.Object3D();
  this.parallaxScale = 5;
  this.parallaxSpeed = 0.1;
  this.fixedTarget = !1;
  this.fixedTargetVector = new THREE.Vector3();
}

CameraDolly.prototype.start = function () {
  this.object3D.add(app.camera);
  app.camera.target = new THREE.Vector3();
};

CameraDolly.prototype.update = function () {
  var t = new THREE.Vector3(),
    i = new THREE.Vector3();

  i.x = +t.x * this.parallaxScale;
  i.y = -t.y * this.parallaxScale;

  if (this.fixedTarget && app.camera.target) {
    this.fixedTargetVector.copy(app.camera.target).sub(this.object3D.position);
    app.camera.lookAt(this.fixedTargetVector);
  } else if (app.camera.target) {
    app.camera.lookAt(app.camera.target);
  }

  this.object3D.position.lerp(i, this.parallaxSpeed);
};

function Application() {
  var scene, camera, renderer, clock;

  /** WebGLRenderer的options
    canvas：指定渲染器将渲染到的HTML canvas元素。
    context：指定渲染器使用的WebGL上下文。
    alpha：指定渲染器是否包含透明度。
    depth：指定渲染器是否包含深度缓冲区。
    stencil：指定渲染器是否包含模板缓冲区。
    antialias：指定是否启用抗锯齿。
    premultipliedAlpha：指定渲染器是否预乘透明度。
    preserveDrawingBuffer：指定是否保留绘图缓冲区。
    powerPreference：指定渲染器的性能偏好。
   */
  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true,
    alpha: true,
  });

  /**
   * 初始值
    fov（视场角）：相机视野的角度，以度为单位。视场角越大，场景中显示的内容越多。
    aspect（宽高比）：相机视锥体的宽高比，通常设置为渲染区域的宽度除以高度。
    near（近裁剪面）：相机视锥体的近裁剪面距离。距离相机小于这个值的物体将不会被渲染。
    far（远裁剪面）：相机视锥体的远裁剪面距离。距离相机大于这个值的物体将不会被渲染。
   */
  camera = new THREE.PerspectiveCamera(10, window.innerWidth / window.innerHeight, 0.1, 1e3);

  /**
   * 可以将各种3D对象（如几何体、光源、相机等）添加到场景中，以便在渲染时呈现这些对象。\
   * scene.add(MeshObject)
   * scene.remove(MeshObject);
   */
  scene = new THREE.Scene();

  scene.camera = camera;

  clock = new THREE.Clock(false);

  this.renderer = renderer;
  this.camera = camera;
  this.scene = scene;
  this.canvas = renderer.domElement;
  this.clock = clock;

  this.animate = this.animate.bind(this);
}

Application.prototype.setup = function () {
  var e = window.innerWidth,
    t = window.innerHeight,
    i = window.devicePixelRatio || 1;

  this.renderer.setPixelRatio(i);
  this.renderer.setSize(e, t, !0);

  if (this.scene.camera === this.camera) {
    this.camera.aspect = e / t;
    this.camera.updateProjectionMatrix();
  }
};

Application.prototype.animate = function () {
  requestAnimationFrame(this.animate);

  onUpdate();
  this.render();
};

Application.prototype.start = function () {
  this.setup();
  this.clock.start();
  onStart();
  this.animate();
};

Application.prototype.render = function () {
  this.renderer.render(this.scene, this.camera);
};

Application.prototype.getTime = function () {
  return this.clock.elapsedTime;
};

Application.prototype.getDelta = function () {
  return this.clock.getDelta();
};

function RewindApplication() {
  window.app = new Application();

  var renderer = app.renderer;
  var scene = app.scene;
  var camera = app.camera;

  camera.fov = 40;
  camera.far = 1e3;
  camera.near = 1;
  camera.updateProjectionMatrix();

  window.dolly = new CameraDolly();
  window.soundwave = new Soundwave();

  var children = [dolly, soundwave];

  children.forEach((child) => {
    scene.add(child.object3D);
    child.object3D.visible = true;
  });

  renderer.compile(scene, camera);

  return app;
}

var renderWebGL = (function () {
  function start() {
    app.start();
  }

  return function (container) {
    RewindApplication();

    if (document.querySelector('#three_canvas')) {
      container.removeChild(document.querySelector('#three_canvas'));
    }

    app.canvas.setAttribute('id', 'three_canvas');
    container.appendChild(app.canvas);

    return {
      start: start,
      transition: function () {},
    };
  };
})();

function avg(arr) {
  for (var t = 0, i = 0; i < arr.length; i++) t += arr[i];
  return t / arr.length;
}

function onStart() {
  dolly.start();
}

function onUpdate() {
  appUniforms.time.value = app.getTime();
  appUniforms.dt.value = app.getDelta();

  var e = avg(threeAudio.frequency) / 256;
  audioUniforms.instantVolume.value = e;
  audioUniforms.smoothedVolume.value += 0.1 * (e - audioUniforms.smoothedVolume.value);

  audioUniforms.waveform.value.needsUpdate = true;
  audioUniforms.frequency.value.needsUpdate = true;

  threeAudio.update();

  dolly.update();
}

export default function (musicUrl, container = document.body, loadedCallback) {
  return new Promise((resolve, reject) => {
    function handleValidAudio() {
      testAudio.volume = 0.0;
      testAudio.pause();
      testAudio.removeEventListener('canplay', handleValidAudio);
      testAudio = undefined;

      window.threeAudio && window.threeAudio.stop();
      window.threeAudio = new AudioSystem(musicUrl, loadedCallback);

      try {
        window.bridge = renderWebGL(container);
        bridge.start();
      } catch (err) {
        console.log('不支持Three.js', err);
      }
      resolve(true);
    }
    function handleErrorAudio() {
      testAudio.removeEventListener('error', handleErrorAudio);
      reject(false);
    }
    let testAudio = document.createElement('audio');
    testAudio.src = musicUrl;
    testAudio.addEventListener('canplay', handleValidAudio);
    testAudio.addEventListener('error', handleErrorAudio);
  });
}
